<!DOCTYPE html>
<html>
<head>
    <title>WebSocket双向检测与波形显示</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8"></script>
    <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@1.2.1"></script>
    <style>
        body { 
            font-family: Arial, sans-serif; 
            margin: 0; 
            padding: 20px; 
            background-color: #f5f5f5;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 { 
            color: #333; 
            text-align: center;
            margin-bottom: 20px;
        }
        .controls { 
            margin: 20px 0; 
            display: flex;
            gap: 10px;
            justify-content: center;
            flex-wrap: wrap;
        }
        button {
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-weight: bold;
            transition: background-color 0.3s;
        }
        button:hover {
            opacity: 0.9;
        }
        #connectBtn { background-color: #4CAF50; color: white; }
        #disconnectBtn { background-color: #f44336; color: white; }
        #zoomInBtn { background-color: #2196F3; color: white; }
        #zoomOutBtn { background-color: #FF9800; color: white; }
        #resetZoomBtn { background-color: #9C27B0; color: white; }
        #status {
            padding: 10px;
            background: #eee;
            border-radius: 5px;
            min-width: 120px;
            text-align: center;
            margin: 10px 0;
        }
        .connected { background-color: #d4edda; color: #155724; }
        .disconnected { background-color: #f8d7da; color: #721c24; }
        #chart-container {
            width: 100%;
            height: 500px;
            margin-top: 20px;
        }
        .stats {
            display: flex;
            gap: 15px;
            justify-content: center;
            margin: 15px 0;
            font-size: 14px;
            color: #666;
        }
        .stat-item {
            background: #f8f9fa;
            padding: 8px 15px;
            border-radius: 5px;
            border-left: 4px solid #4CAF50;
        }
        .ping-stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 10px;
            margin: 15px 0;
        }
        .ping-stat {
            background: #f8f9fa;
            padding: 10px;
            border-radius: 5px;
            text-align: center;
        }
        .ping-value {
            font-size: 24px;
            font-weight: bold;
            color: #2c3e50;
        }
        .ping-label {
            font-size: 12px;
            color: #7f8c8d;
        }
        #messages {
            margin-top: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
            padding: 15px;
            height: 200px;
            overflow-y: auto;
            background-color: #fafafa;
        }
        .message {
            padding: 5px;
            margin: 3px 0;
            border-radius: 3px;
            border-left: 4px solid;
        }
        .send { border-left-color: #2196F3; background-color: #e3f2fd; }
        .receive { border-left-color: #4CAF50; background-color: #e8f5e8; }
        .error { border-left-color: #f44336; background-color: #ffebee; }
    </style>
</head>
<body>
    <div class="container">
        <h1>WebSocket双向检测与实时波形显示 (20分钟数据)</h1>
        
        <div class="ping-stats">
            <div class="ping-stat">
                <div class="ping-value" id="pingsvrreqCount">0</div>
                <div class="ping-label">收到pingsvrreq</div>
            </div>
            <div class="ping-stat">
                <div class="ping-value" id="pongclirespCount">0</div>
                <div class="ping-label">发送pongcliresp</div>
            </div>
            <div class="ping-stat">
                <div class="ping-value" id="pingclireqCount">0</div>
                <div class="ping-label">发送pingclireq</div>
            </div>
            <div class="ping-stat">
                <div class="ping-value" id="pongsvrrespCount">0</div>
                <div class="ping-label">收到pongsvrresp</div>
            </div>
            <div class="ping-stat">
                <div class="ping-value" id="responseTime">0ms</div>
                <div class="ping-label">平均响应时间</div>
            </div>
        </div>
        
        <div class="stats">
            <div class="stat-item">采样率: <span id="sampleRate">1000Hz</span></div>
            <div class="stat-item">精度: <span id="bits">16位</span></div>
            <div class="stat-item">数据点: <span id="pointCount">0</span></div>
            <div class="stat-item">总点数: <span id="totalPoints">0</span></div>
            <div class="stat-item">当前时间: <span id="currentTime">0s</span></div>
            <div class="stat-item">更新时间: <span id="lastUpdate">-</span></div>
        </div>
        
        <div class="controls">
            <button id="connectBtn">连接WebSocket</button>
            <button id="disconnectBtn">断开连接</button>
            <button id="zoomInBtn">放大</button>
            <button id="zoomOutBtn">缩小</button>
            <button id="resetZoomBtn">重置缩放</button>
        </div>
        
        <div id="status" class="disconnected">状态: 未连接</div>
        
        <div id="chart-container">
            <canvas id="waveformChart"></canvas>
        </div>
        
        <div id="messages"></div>
    </div>

    <script>
        // 初始化Chart.js
        const ctx = document.getElementById('waveformChart').getContext('2d');
        const chart = new Chart(ctx, {
            type: 'line',
            data: {
                datasets: [{
                    label: '波形数据 (uint16_t)',
                    borderColor: 'rgb(75, 192, 192)',
                    backgroundColor: 'rgba(75, 192, 192, 0.1)',
                    borderWidth: 1,
                    pointRadius: 0,
                    fill: true,
                    tension: 0.1,
                    data: []
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    x: {
                        type: 'linear',
                        position: 'bottom',
                        title: { 
                            display: true, 
                            text: '时间 (秒)',
                            font: { weight: 'bold' }
                        },
                        min: 0,
                        max: 1200  // 20分钟=1200秒
                    },
                    y: {
                        title: { 
                            display: true, 
                            text: '幅值',
                            font: { weight: 'bold' }
                        },
                        min: 0,
                        max: 65535  // uint16_t范围
                    }
                },
                plugins: {
                    legend: {
                        display: true,
                        position: 'top'
                    },
                    zoom: {
                        pan: {
                            enabled: true,
                            mode: 'xy',
                            modifierKey: 'ctrl'
                        },
                        zoom: {
                            wheel: {
                                enabled: true
                            },
                            pinch: {
                                enabled: true
                            },
                            mode: 'xy',
                        }
                    }
                },
                animation: {
                    duration: 0
                }
            }
        });

        // WebSocket连接管理
        let ws = null;
        let dataBuffer = [];
        const maxPoints = 1200000; // 20分钟数据 (1000Hz * 60 * 20)
        let timeCounter = 0;
        let pointCount = 0;
        let totalPoints = 0;
        
        // 心跳统计
        let pingsvrreqCount = 0;
        let pongclirespCount = 0;
        let pingclireqCount = 0;
        let pongsvrrespCount = 0;
        let responseTimes = [];
        let lastPingsvrreqTime = null;
        let pingclireqInterval = null;
        
        // 连接WebSocket
        function connect() {
            if (ws) {
                addMessage('已经连接了', 'error');
                return;
            }
            
            console.log('🌐 开始连接WebSocket...');
            addMessage('正在连接WebSocket...');
            
            ws = new WebSocket(`ws://${window.location.host}/ws`);
            
            ws.onopen = () => {
                updateStatus('connected');
                addMessage('✅ WebSocket连接成功', 'receive');
                console.log('🎉 WebSocket连接已建立');
                
                // 重置数据
                dataBuffer = [];
                timeCounter = 0;
                pointCount = 0;
                chart.data.datasets[0].data = [];
                chart.update('none');
                
                // 启动前端定时发送pingclireq
                startPingclireqInterval();
            };
            
            ws.onclose = () => {
                updateStatus('disconnected');
                addMessage('❌ WebSocket连接断开', 'error');
                console.log('🔴 WebSocket连接已关闭');
                ws = null;
                
                // 清除定时器
                clearInterval(pingclireqInterval);
            };
            
            ws.onerror = (error) => {
                console.error('❌ WebSocket错误:', error);
                addMessage('❌ WebSocket连接错误', 'error');
                ws = null;
            };
            
            ws.onmessage = (event) => {
                try {
                    // 处理JSON数据
                    if (event.data.startsWith('{')) {
                        const data = JSON.parse(event.data);
                        
                        if (data.type === 'waveform') {
                            // 更新统计信息
                            document.getElementById('sampleRate').textContent = data.sample_rate + 'Hz';
                            document.getElementById('bits').textContent = data.bits + '位';
                            pointCount += data.data.length;
                            totalPoints = data.total_points;
                            document.getElementById('pointCount').textContent = pointCount;
                            document.getElementById('totalPoints').textContent = totalPoints;
                            document.getElementById('currentTime').textContent = (data.current_index / data.sample_rate).toFixed(2) + 's';
                            document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString();
                            
                            // 处理波形数据
                            data.data.forEach(value => {
                                dataBuffer.push({x: timeCounter / 1000, y: value}); // 转换为秒
                                timeCounter += 1; // 1ms间隔
                            });
                            
                            // 保持数据量不超过maxPoints
                            if (dataBuffer.length > maxPoints) {
                                dataBuffer = dataBuffer.slice(dataBuffer.length - maxPoints);
                            }
                            
                            // 更新图表
                            chart.data.datasets[0].data = dataBuffer;
                            chart.update('none');
                        }
                    } 
                    // 处理文本消息
                    else {
                        console.log('📨 收到消息:', event.data);
                        addMessage('📨 收到: ' + event.data, 'receive');
                        
                        // 处理服务器发送的pingsvrreq
                        if (event.data === 'pingsvrreq') {
                            pingsvrreqCount++;
                            lastPingsvrreqTime = Date.now();
                            console.log('🔄 收到pingsvrreq，准备发送pongcliresp');
                            document.getElementById('pingsvrreqCount').textContent = pingsvrreqCount;
                            
                            // 自动响应pingsvrreq
                            setTimeout(() => {
                                sendPongcliresp();
                            }, 100);
                        }
                        
                        // 处理服务器响应的pongsvrresp
                        else if (event.data === 'pongsvrresp') {
                            pongsvrrespCount++;
                            console.log('✅ 收到pongsvrresp');
                            document.getElementById('pongsvrrespCount').textContent = pongsvrrespCount;
                        }
                        
                        // 处理ack确认
                        else if (event.data === 'ack') {
                            if (lastPingsvrreqTime) {
                                const responseTime = Date.now() - lastPingsvrreqTime;
                                responseTimes.push(responseTime);
                                
                                // 计算平均响应时间
                                if (responseTimes.length > 0) {
                                    const avgTime = Math.round(responseTimes.reduce((a, b) => a + b) / responseTimes.length);
                                    document.getElementById('responseTime').textContent = avgTime + 'ms';
                                }
                            }
                        }
                    }
                } catch (e) {
                    console.error('消息处理错误:', e);
                }
            };
        }
        
        // 启动前端定时发送pingclireq
        function startPingclireqInterval() {
            // 先清除已有定时器
            clearInterval(pingclireqInterval);
            
            // 每40秒发送一次pingclireq
            pingclireqInterval = setInterval(() => {
                if (ws && ws.readyState === WebSocket.OPEN) {
                    sendPingclireq();
                }
            }, 40000);
        }
        
        // 发送pingclireq到服务器
        function sendPingclireq() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                pingclireqCount++;
                console.log('🔼 发送pingclireq到服务器');
                document.getElementById('pingclireqCount').textContent = pingclireqCount;
                ws.send('pingclireq');
                addMessage('📤 发送: pingclireq', 'send');
            }
        }
        
        // 发送pongcliresp响应服务器
        function sendPongcliresp() {
            if (ws && ws.readyState === WebSocket.OPEN) {
                pongclirespCount++;
                console.log('🔄 发送pongcliresp响应');
                document.getElementById('pongclirespCount').textContent = pongclirespCount;
                ws.send('pongcliresp');
                addMessage('📤 发送: pongcliresp', 'send');
            }
        }
        
        // 断开连接
        function disconnect() {
            if (ws) {
                console.log('手动断开WebSocket连接');
                ws.close();
                ws = null;
                
                // 清除定时器
                clearInterval(pingclireqInterval);
            }
        }
        
        // 更新状态显示
        function updateStatus(status) {
            const statusDiv = document.getElementById('status');
            statusDiv.textContent = '状态: ' + 
                (status === 'connected' ? '已连接' : '未连接');
            statusDiv.className = status;
        }
        
        // 添加消息到消息区域
        function addMessage(message, type = 'receive') {
            const messagesDiv = document.getElementById('messages');
            const div = document.createElement('div');
            div.className = `message ${type}`;
            div.innerHTML = `
                <strong>${new Date().toLocaleTimeString()}</strong> - ${message}
            `;
            messagesDiv.appendChild(div);
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }
        
        // 缩放控制
        document.getElementById('zoomInBtn').addEventListener('click', () => {
            chart.zoom(1.2);
        });
        
        document.getElementById('zoomOutBtn').addEventListener('click', () => {
            chart.zoom(0.8);
        });
        
        document.getElementById('resetZoomBtn').addEventListener('click', () => {
            chart.resetZoom();
        });
        
        // 按钮事件
        document.getElementById('connectBtn').addEventListener('click', connect);
        document.getElementById('disconnectBtn').addEventListener('click', disconnect);
        
        // 页面加载时自动连接
        window.addEventListener('load', () => {
            console.log('🌐 页面加载完成');
            connect();
        });
    </script>
</body>
</html>